#include "Dri_I2C2.h"

/**
 * @brief I2C 初始化
 *
 */
void Dri_I2C2_Init(void)
{
    // 1. 时钟使能 --------------------------------
    // 1.1 对 I2C2 时钟使能
    RCC->APB1ENR |= RCC_APB1ENR_I2C2EN;
    // 1.2 对 GPIOB 时钟使能
    RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;

    // 2. 引脚模式设置 ----------------------------
    // 设置 SCL(PB10)、SDA（PB11）设置为复用开漏输出，MODE=11; CNF=11
    GPIOB->CRH |= (GPIO_CRH_MODE10 | GPIO_CRH_CNF10 | GPIO_CRH_MODE11 | GPIO_CRH_CNF11);

    // 3. I2C2 设置  ----------------------------
    // 3.1 复位 I2C2 模块(可省略)
    I2C2->CR1 |= I2C_CR1_SWRST;
    I2C2->CR1 &= ~I2C_CR1_SWRST;
    // 3.2 设置 I2C2 的输入时钟频率是 APB1 的 36MHz
    I2C2->CR2 &= ~I2C_CR2_FREQ;
    I2C2->CR2 |= 36;
    // 3.3 时钟控制寄存器配置：选择标准模式（默认值）
    I2C2->CCR &= ~I2C_CCR_FS;
    // 3.4 时钟控制寄存器配置：时钟控制分频系数； 实现标准模式传输速度100kbit/s, CCR需要设置为180
    I2C2->CCR &= ~I2C_CCR_CCR;
    I2C2->CCR |= 180;
    // 3.5 设置标准模式下SCL的最大上升沿时间，需要将值设置为 37    1000ns / T_pclk1 + 1 = 37
    I2C2->TRISE = 37;
    // 3.6 设置使用I2C模式不使用SMBus(默认值)
    I2C2->CR1 &= ~I2C_CR1_SMBUS;
    // 3.7 I2C2 使能
    I2C2->CR1 |= I2C_CR1_PE;
}

/**
 * @brief 发送起始信号
 *
 */
void Dri_I2C2_Start(void)
{
    // 设置控制位以发送起始信号
    I2C2->CR1 |= I2C_CR1_START;

    // 等待起始信号发送完成，如果SB为0就一直循环等待
    while ((I2C2->SR1 & I2C_SR1_SB) == 0)
        ;
}

/**
 * @brief 发送停止信号
 *
 */
void Dri_I2C2_Stop(void)
{
    I2C2->CR1 |= I2C_CR1_STOP;
}

/**
 * @brief 发送从设备地址和读写标识
 *
 * @param addr 从设备地址和读写标识
 */
void Dri_I2C2_TransmitAddress(uint8_t addr)
{
    // 写入数据寄存器，开始发送
    I2C2->DR = addr;

    // 等待地址发送完成，如果标志位ADDR为0就一直循环等待
    while ((I2C2->SR1 & I2C_SR1_ADDR) == 0)
        ;

    // 读SR2以清除 ADDR 标志位
    I2C2->SR2;
}

/**
 * @brief 发送一个字节的数据
 *
 * @param byte 要发送的字节
 */
void Dri_I2C2_TransmitByte(uint8_t byte)
{
    // 将要发送的字节数据写入数据寄存器
    I2C2->DR = byte;

    // 等待该字节发送完成，如果标志位BTF为0就一直循环等待
    while ((I2C2->SR1 & I2C_SR1_BTF) == 0)
        ;
}

/**
 * @brief 接收一个应答信号
 *
 * @return uint8_t 接收到的应答信号，1表示NACK，0表示ACK
 */
uint8_t Dri_I2C2_ReceiveACK(void)
{
    // 保存下应答信号标志位
    uint8_t ack = (I2C2->SR1 & I2C_SR1_AF) ? 1 : 0;
    // 清除 AF 标志位
    I2C2->SR1 &= ~I2C_SR1_AF;
    // 返回ack
    return ack;
}

/**
 * @brief 接收一个字节的数据
 *
 * @return uint8_t 接收到的字节
 */
uint8_t Dri_I2C2_ReceiveByte(void)
{
    // 等待数据接收完毕，标志位RXNE如果为0就一直循环等待
    while ((I2C2->SR1 & I2C_SR1_RXNE) == 0)
        ;
    
    // 读取DR，并返回
    return I2C2->DR;
}

/**
 * @brief 发送ACK信号
 *
 * @param ack 要发送的ACK信号, 0表示ACK，1表示NACK
 */
void Dri_I2C2_TransmitACK(uint8_t ack)
{
    if (ack == 0)
    {
        I2C2->CR1 |= I2C_CR1_ACK;
    }
    else
    {
        I2C2->CR1 &= ~I2C_CR1_ACK;
    }
}
